package org.hackystat.devcathlon.engine.event;

import java.io.Serializable;
import java.text.DateFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.xml.datatype.XMLGregorianCalendar;
import org.hackystat.devcathlon.engine.match.Match;
import org.hackystat.devcathlon.engine.match.MatchManager;
import org.hackystat.devcathlon.engine.team.Team;
import org.hackystat.devcathlon.engine.user.User;
import org.hackystat.utilities.tstamp.Tstamp;

/**
 * Provides a representation of the results of invoking an Event on a specific team at a specific
 * time in a specific match.
 * 
 * @author Philip Johnson
 */
public class EventScore implements Serializable {
  /** Support serialization. */
  private static final long serialVersionUID = 1L;
  /** Primary key reference id. */
  private Long id;
  /** The base score used to calculate points. */
  private int baseScore = 0;
  /** An explanation for this score. */
  private String explanation = "No explanation for this score was provided.";
  /** The time at which this event was scored. */
  private String timestamp;
  /** The match when this event occurred. */
  private String matchName;
  /** The team that this event score is associated with. */
  private Team team;
  /** The user that this event score is associated with. */
  private User user;
  /** A map of any bonus points obtained by a team for this event score. */
  private Map<String, Integer> bonusScoreMap = new HashMap<String, Integer>();

  /**
   * Represents the results of invoking an Event on a Team during a Match.
   * 
   * @param timestamp The timestamp when the Event was scored.
   * @param match The Match associated with this Event and score.
   * @param team The Team who was scored.
   */
  public EventScore(XMLGregorianCalendar timestamp, Match match, Team team) {
    this.timestamp = timestamp.toString();
    this.matchName = match.getName();
    this.team = team;
  }

  /**
   * Represents the results of invoking an Event on a User on a Team during a Match.
   * 
   * @param timestamp The timestamp when the Event was scored.
   * @param match The Match associated with this Event and score.
   * @param team The Team who was scored.
   * @param user The User who was scored.
   */
  public EventScore(XMLGregorianCalendar timestamp, Match match, Team team, User user) {
    this.timestamp = timestamp.toString();
    this.matchName = match.getName();
    this.team = team;
    this.user = user;
  }
  
  /**
   * Returns the unique id.
   * @return the id
   */
  public Long getId() {
    return id;
  }

  /**
   * Sets the unique id.
   * @param id the id to set
   */
  private void setId(Long id) {
    this.id = id;
  }

  /**
   * Returns the base score.
   * 
   * @return The base score.
   */
  public int getBaseScore() {
    return this.baseScore;
  }

  /**
   * Sets the baseScore to baseScore.
   * 
   * @param baseScore The new baseScore.
   */
  public void setBaseScore(int baseScore) {
    this.baseScore = baseScore;
  }

  /**
   * Returns the explanation.
   * 
   * @return The explanation.
   */
  public String getExplanation() {
    return this.explanation;
  }

  /**
   * Sets the explanation string.
   * 
   * @param explanation The new explanation string.
   */
  public void setExplanation(String explanation) {
    this.explanation = explanation;
  }

  /**
   * Adds additional explanation. Appends the passed string to the current explanation, separated
   * with a space.
   * 
   * @param additionalExplanation The additional explanation.
   */
  public void appendExplanation(String additionalExplanation) {
    this.explanation = this.explanation + " " + additionalExplanation;
  }

  /**
   * Adds a bonus score and its associated points to this EventScore instance.
   * 
   * @param bonusScoreName The name of the bonus.
   * @param bonusScoreValue The value of this bonus.
   */
  public void addBonusScore(String bonusScoreName, int bonusScoreValue) {
    this.bonusScoreMap.put(bonusScoreName, bonusScoreValue);
  }

  /**
   * Returns the map of bonus scores and their point values.
   * 
   * @return The map of bonus scores.
   */
  public Map<String, Integer> getBonusScoreMap() {
    return this.bonusScoreMap;
  }
  
  /**
   * Sets the map of bonus scores and their point values.
   * 
   * @param bonusScoreMap The map of bonus scores.
   */
  private void setBonusScoreMap(Map<String, Integer> bonusScoreMap) {
    this.bonusScoreMap = bonusScoreMap;
  }

  /**
   * Returns the bonus score given the name, or 0 if the bonus score name is not present in the map.
   * 
   * @param bonusScoreName The bonus score name.
   * @return The score if present, or 0 if not present.
   */
  public int getBonusScore(String bonusScoreName) {
    return (this.bonusScoreMap.containsKey(bonusScoreName)) ? this.bonusScoreMap
        .get(bonusScoreName) : 0;
  }

  /**
   * Returns true if this event score has bonus scores.
   * 
   * @return True if there are bonuses to be had.
   */
  public boolean hasBonusScores() {
    return !this.bonusScoreMap.isEmpty();
  }

  /**
   * Returns the timestamp when this score was assessed.
   * 
   * @return The timestamp.
   * @throws Exception If there is an error with Tstamp
   */
  public XMLGregorianCalendar getTimestampDate() throws Exception {
    return Tstamp.makeTimestamp(this.getTimestamp());
  }
  
  /**
   * Set the timestamp for when this score was assessed.
   *  
   * @param timestamp The timestamp (String).
   */
  private void setTimestamp(String timestamp) {
    this.timestamp = timestamp;
  }
  
  /**
   * Returns the timestamp when this score was assessed.
   * 
   * @return The timestamp (String).
   */
  public String getTimestamp() {
    return this.timestamp;
  }
  
  /**
   * Returns a printable string of the date and time when this score was assessed.
   * 
   * @return The date and time in the format (mm/dd/yy  hh:mm pm/am).
   * @throws Exception If there is an error error with the timestamp.
   */
  public String printDateTime() throws Exception {
    DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,
        new Locale("en", "US"));
    return formatter.format(this.getTimestampDate().toGregorianCalendar().getTime());
  }

  /**
   * Returns the Match associated with this score.
   * 
   * @return The match.
   */
  public Match getMatch() {
    return MatchManager.getInstance().getMatch(matchName);
  }
  
  /**
   * Returns the match name.
   * 
   * @return The match name.
   */
  public String getMatchName() {
    return this.matchName;
  }

  /**
   * Sets the match name.
   * 
   * @param match the match to set
   */
  private void setMatchName(String match) {
    this.matchName = match;
  }

  /**
   * Returns the team who got this score.
   * 
   * @return The team.
   */
  public Team getTeam() {
    return this.team;
  }
  
  /**
   * Sets the team who gets this score.
   * 
   * @param team The team.
   */
  private void setTeam(Team team) {
    this.team = team;
  }
  
  /**
   * Returns the user who got this score.
   * 
   * @return The user.
   */
  public User getUser() {
    return this.user;
  }
  
  /**
   * Sets the user who gets this score.
   * 
   * @param user The user.
   */
  private void setUser(User user) {
    this.user = user;
  }
}
